#pragma once

#include "RoomDefines.h"

namespace RoomFW {

//
// Class:	IPayload
//
// Description:	Base message payload interface.
//
// Author:	Andrew Sage
//
class IPayload;
typedef public ::boost::shared_ptr<IPayload> IPayloadSP;
class IPayload
{
public:

	//
	// Method:	~IPayload
	//
	// Description:	Virtual destructor to ensure derived class destructors called.
	//
	virtual ~IPayload() {};

	//
	// Method:	isSafe
	//
	// Description:	Check if this payload is safe to send.  It is save if
	//				IPayload::IsSafe() returns true for all IPayload derived
	//				attributes.  Derived classes must implement accordingly.
	//
	// Parameters:	None.
	//
	// Return:	True if this object is safe to send as a message payload.
	//
	virtual bool isSafe(void) const = 0;

	//
	// Method:	getTypeID
	//
	// Description:	Get the type ID for this class.  Derived classes must implement.
	//
	// Parameters:	None.
	//
	// Return:	The type ID for this class.
	//
	virtual PayloadTypeId getTypeID(void) const = 0;
	
	//
	// Method:	toString
	//
	// Description:	Get a text representation of a payload.
	//
	// Parameters:	None.
	//
	// Return:	Text representation of payload.
	//
	virtual std::string toString(void) const = 0;
	
	/// Give Boost access so it can handle serialization.
	/// See http://www.boost.org/doc/libs/1_39_0/libs/serialization/doc/tutorial.html
	friend class boost::serialization::access;

	//
	// Method:	serialize
	//
	// Description:	Serialize this object.  Derived classes must implement if
	//				they are to be serialized.
	//
	// Parameters:
	//
	//	ar - Boost out archive.
	//
	//	version - ignored.
	//
	// Return:	None.
	//
	virtual void serialize(oarchive& /*ar*/, const unsigned int /*version*/) const;

	//
	// Method:	serialize
	//
	// Description:	Un-Serialize this object.  Derived classes must implement if
	//				they are to be serialized.
	//
	// Parameters:
	//
	//	ar - Boost in archive.
	//
	//	version - ignored.
	//
	// Return:	None.
	//
	virtual void serialize(iarchive& /*ar*/, const unsigned int /*version*/) ;

	//
	// Method:	clone
	//
	// Description:	Return a copy of this.  Implement if you need it.
	//
	// Parameters:	None
	//
	// Return:	Copy of this.  E.g. return IPayloadSP(new MyClass(*this));
	//
	virtual IPayloadSP clone() { ASSERT(false); return IPayloadSP(); }

	//
	// Method:	ReleaseOwnership
	//
	// Description:	Release ownership of an IPayload derived object.  Use to wrap
	//				calls to ActiveObject::sendMessage() and the like.
	//
	// Parameters:
	//
	//	payload - IPayload derived smart pointer.
	//
	// Return:	A IPayloadSP to the input parameter.
	//
	template<typename PAYLOAD_SP_T>
	static IPayloadSP ReleaseOwnership(PAYLOAD_SP_T& payload)
	{
		IPayloadSP retVal(payload);
		payload.reset();
		return retVal;
	}

	//
	// Method:	ReleaseCast
	//
	// Description:	Release ownership of any smart pointer type and cast
	//				to specified return smart pointer type.  If the cast
	//				is not valid, it returns null and does not release
	//				ownership of the input parameter.
	//
	// Parameters:
	//
	//	RETURN_SP_T - Return type.  Must be a boost::shared_ptr<>
	//
	//	IN_SP_T - Parameter type.  Must be a boost::shared_ptr<>
	//
	//	ptr - Smart pointer to cast and release.  Let's hear the fishing puns...
	//
	// Return:	Input parameter cast to specified type and reference passed in reset.
	//
	template<typename RETURN_SP_T, typename IN_SP_T>
	static RETURN_SP_T ReleaseCast(IN_SP_T& ptr)
	{
		RETURN_SP_T retVal = boost::dynamic_pointer_cast<RETURN_SP_T::element_type>(ptr);
		if (retVal)
		{
			ptr.reset();
		}
		return retVal;
	}

	//
	// Method:	IsSafe
	//
	// Description:	Check if an IPayload derived object is safe to send.
	//				It is save if it has a reference count of one,
	//				the correct type id and its own isSafe() returns true.
	//
	// Parameters:
	//
	//	typeId - Type of payload expected.
	//
	//	payload - The object to check.
	//
	// Return:	True if input object is safe to send as a message payload.
	//
	template<typename PAYLOAD_SP_T>
	static bool IsSafe(PayloadTypeId typeId, const PAYLOAD_SP_T& payload)
	{
		ASSERT(payload.unique());
		ASSERT(payload->getTypeID() == typeId);
		ASSERT(payload->isSafe());
		return (payload.unique() && (payload->getTypeID() == typeId) && payload->isSafe());
	}
};

}

// Helper macros.
#define PAYLOAD1(type, a1) RoomFW::IPayload::ReleaseOwnership(RoomFW::IPayloadSP(new type((a1))))
#define PAYLOAD2(type, a1, a2) RoomFW::IPayload::ReleaseOwnership(RoomFW::IPayloadSP(new type((a1), (a2))))
#define PAYLOAD3(type, a1, a2, a3) RoomFW::IPayload::ReleaseOwnership(RoomFW::IPayloadSP(new type((a1), (a2), (a3))))
#define PAYLOAD4(type, a1, a2, a3, a4) RoomFW::IPayload::ReleaseOwnership(RoomFW::IPayloadSP(new type((a1), (a2), (a3), (a4))))
#define PAYLOAD5(type, a1, a2, a3, a4, a5) RoomFW::IPayload::ReleaseOwnership(RoomFW::IPayloadSP(new type((a1), (a2), (a3), (a4), (a5))))
#define PAYLOAD6(type, a1, a2, a3, a4, a5, a6) RoomFW::IPayload::ReleaseOwnership(RoomFW::IPayloadSP(new type((a1, (a2), (a3), (a4), (a5), (a6)))))
#define PAYLOAD7(type, a1, a2, a3, a4, a5, a6, a7) RoomFW::IPayload::ReleaseOwnership(RoomFW::IPayloadSP(new type((a1), (a2), (a3), (a4), (a5), (a6), (a7))))
#define PAYLOAD8(type, a1, a2, a3, a4, a5, a6, a7, a8) RoomFW::IPayload::ReleaseOwnership(RoomFW::IPayloadSP(new type((a1), (a2), (a3, (a4), (a5), (a6), (a7), (a8)))))
#define PAYLOAD9(type, a1, a2, a3, a4, a5, a6, a7, a8, a9) RoomFW::IPayload::ReleaseOwnership(RoomFW::IPayloadSP(new type((a1), (a2), (a3), (a4), (a5), (a6), (a7), (a8), (a9))))

//
// Can't use this boost macro in a namespace because it declares the boost namespace.
//
//BOOST_CLASS_TRACKING(RoomFW::IPayload, ::boost::serialization::track_never);
